business project向け監査ログ出力はlean()をつけるべき
from scrapbox.ioのみ時間経過でメモリ使用量が増加する原因を、Codex CLIとともに探る
Audit Logs の CSV 生成では src/server/controllers/projects/audit-logs.js:131-135 で users キャッシュに User.findOne の結果(設定やタイムスタンプなどを含む Mongoose Document)を丸ごと保持しており、必要なのはメールアドレスだけなのに大量のオブジェクトがリクエスト中ずっと残る
.select('email').lean() でメール文字列だけを保持するか、事前にユニークな userId をまとめて取得してキャッシュ自体をやめるべき。
src/server/controllers/projects/audit-logs.js:138-175 では Event を .lean() なしでストリームし、さらに event.data を mutate しているため、各バッチが change-tracking 付きのmongoose.Document を生成し続け、行単価のメモリが跳ね上がる。
.select(...) と .lean() を使った cursor({ batchSize }) に切り替えるとドキュメントラッパーを避けられ、ヒープへの常駐量が大幅に減る。
同じ箇所でレスポンスの backpressure を res.writableLength のポーリングに頼っており、クライアントが遅いとカーソルが既定バッチ(~1000件)+ユーザキャッシュを先読みしてしまう。HTTPストリームの drain イベントに合わせて cursor.pause()/resume() を呼ぶか、バッチサイズを縮めてフラッシュ速度より先にデータが溜まらないように調整する必要がある。
次のステップ
1. users のキー数やカーソルのバッチ統計、ダウンロード前後のヒープスナップショットなどの計測ログを追加し、どの割り当てが支配的か確認する。
2. lean/projection とメールのみキャッシュの試作を行い、本番相当データで /api/projects/auditlogs/... を叩き、node --inspect などでヒープ/CPU を比較する。
3. それでも増える場合はエクスポート前後のヒープスナップショットを差分取得し、残留オブジェクト(model:User/model:Event など)を特定してバッチサイズや backpressure 制御をさらに調整する。
たぶん、Helpfeel専用Cosense Export APIはMongoDB query cursorを閉じ忘れていると同じ問題もありそうなのであっちを先にやろうshokai.icon
この最適化はたぶんやりすぎ、ここまでやるならbusiness project向け監査ログを動的生成せずにファイル出力してGCSにアップロードしておくようにするべき